Day 4: Shiny apps

Gentle introduction

Ben Fanson https://bfanson.github.io/2024DADAworkshop/ (Arthur Rylah Institute)https://www.ari.vic.gov.au/
2024-09-18

REMINDER TO US: START RECORDING

Before starting

Make sure you have the shiny package installed on your computer. You can use code below or the “Packages” tab in lower right panel in Rstudio.

Day’s learning objectives

  1. Have a good understanding of what Shiny apps can do and how it might be useful for you

  2. Understand the core structure of Shiny apps: UI, Server, Reactivity

  3. Learn how to create and run an App locally as well as deploy on ARI’s shiny account

  4. Feel confident enough to grab example code from Rshiny gallery 🤞

Key packages

Of course there is cheatsheet to check out…


knitr::include_graphics("shiny.pdf")











Project folder for today

For background information on the dataset, see dataset summary

Pep talk

Okay, before we get started, I think that it is useful to give a pep talk. For the more programming-phobic of you, the Shiny code will look foreign. You first reaction might be “uggg, I am a researcher, not a programmer and I do not want to be programmer” and “are you serious after all this time I spent learning R coding and now I have to learn another type of programming??”

But, we swear that it is not a bad at it looks. Once you get beyond the foreignness of the code, we guarantee that with a little guidance, you can easily get started making your own apps in minutes. It is just about getting the basic structure and then having the confidence to do “trial-and-error” with borrowed code. Just be forewarned, shiny Apps can become addicting!!!

Use of AI

I will be using AI a fair a bit today. I have found really useful for getting started with shiny apps and for the basics (mostly what we covering here), it can really help. As noted in other days, it is helpful to have a few terminology/concepts under your belt when working with AI for shiny app creation. I will try to demonstrate this through this session. I will just work with Copilot (and maybe Claude for comparison?). I have more info in the page on AI extra topic.

Let’s build one together

Go through a simple example

We will work through an example of building a simple app that displays a graph depending on the conditions selected.

I am going to work with the built-in mtcars dataset for the example. I want an user to select a the number of cylinders a car has and plot the relationship between horsepower(hp) and fuel efficiency (mpg).

                   mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

In Copilot, I typed: “build me a shiny app using the mtcars dataset. The UI will have a side panel that will allow the user to select the cyl type and the main panel will have two tabs. The first tab will show a plot of the relationship between hp and mpg for the cyl selected by the user. The plot will also have the linear regression line and confidence band that was created using lm(). The second tab will have the linear model outputs extracted from the model using broom::tidy()”

The coding output are shown below:

It does run successfully and the UI looks like the following:

Walk through the key bits

Setting up the app

ONE vs MULTIPLE files

For your app setup, you can either select a single file called app.R [like we did in the example] or you can create two files: ui.R and server.R. What this is doing is just breaking up the one file into two, otherwise basically the same. The advantage of the latter is in larger apps and for reusing code. We will stick with one file method for rest of today.

Structure of the file

Shiny app has a straightforward structure comprising four main components: 1) global heading; 2) ui; 3) server; and 4) shinyApp.

Visual model of the file…

Let’s go through each component…

UI (= user interface)

This is what the user will be using to explore your data/results/visualizations.

Step 1: pick a layout for UI

First step is determining what kind of layout you want. Single panel, main panel with sidepanel, tabs. May want to check Shiny gallery or layout for ideas.

In our example, AI produced the following UI code using fluidPage() then breaking into sidebarLayout() and then again into sidebarPanel() and mainPanel(). Within the mainPanel() section, it added tabsetPanel() to get the tabs.

Step 2: create the inputs objects: buttons, inputs, drop-down menu

Once layout is figured out, now you add in the input objects. In our example that was a drop-down menu that allowed the user to pick cylinder size.

Check the cheatsheet for basic options for inputs or search Shiny gallery

Few key takeways

input

In the UI, an input list is created from the UI design and it is used to send user inputs to the server.

Takeways

server

The server is the heart of the app. Let revisit the visual model…

Three main key bits…

  1. You have the inputs coming in that will be used

  2. R code that will take the inputs and convert to new output(s)

  3. the output list is created and exported back to UI

Let’s look a little closer at the server code

Key takeways

  1. not separated by commas

  2. more like R programming but order does not matter between components!!!!!

  3. Within renderXXX or reactive() R code and order does matter here. Note the double-brackets needed when multiple lines

output

Our the server created output$hp_mpg_plot using renderPlot and output$model_summary using renderTable and the ui outputs using plotOutput and tableOutput. You get these pairs of renderXXX({}) and xxxOutput().

Look at the cheatsheet for a partial list of pairings…

Note - this is not a comprehensive list and packages might have then own renderXxx() and xxxOutput()

Couple more examples…

A few tips on debugging

Little more on the server side

Reactivity

The style of programming used in shiny apps is called “reactive programming”. The purpose of this programming style is to keep inputs and outputs in sync. To do this efficiency, you only want to update outputs that need to be changed. So the rule is “change if and only if inputs change”.

In our example we had one such case. This reactive function was then used in both renderXXX functions.

The two key components of this are called:

You will see a variety of functions that are doing “extra” bits in the programming:

We will not go into depth here but just want you to be aware and when you see them, know that they are doing extra stuff and a bit of efficiency stuff. For more complicated apps, they are essential to get a handle of.

Shiny Extensions

As noted about, search the Shiny gallery to get ideas of additional options of what you can do. On this website, it provides all the ui/server code on a GitHub repository, so you can just grab the code.

Another good place to search for additional extensions for shiny is Shiny Awesome which has a curated list and some of the stuff, is well, awesome! (and overwhelming) The links often lead you to GitHub pages.

Useful interactive packages for shiny

As Shiny apps are all about interactivity, you may want to use interactive plots. Check the following webpage for a examples of packages that have interactivity.

I will show a few common ones below:

leaflet

mapview package

The mapview package can be used to set up a simple, quick leaflet. This would be most useful when the leaflet is created on startup (not adding in reactivity like adding or deleting data being shown). For instance, you might want to show all the sites surveyed and attach metadata (e.g. dates surveyed, number of fish caught, number of plots) to that point.

Useful shiny functions for Server/UI…

  mapview::renderMapview()  # server side
  mapview::mapviewOutput()  # UI side

leaflet package

Now, if you are going to build maps that will change with user input, it is best to build from “scratch” using leaflet package (and probably leaflet.extra package + others)

When you have user inputs affecting the map shown, you want to try to avoid rebuilding the map object and rather just modify the elements that the user wants changed (e.g. drop lines and add points instead). This requires using reactive programming and observe() functions. Check out Nev’s ibis app that does this. Below just shows simple leaflet code:

Useful shiny functions for Server/UI…

  leaflet::leafletOutput()  # UI side
  leaflet::renderLeaflet()  # server side - creates the basemap
  leaflet::leafletProxy()   # this is the server function that updates the map 

plotly

Now, plotly package does a great job at interactive plots. You can write you code in ggplot and then just convert as shown in code below.

  library(ggplot2)
  library(plotly)
  f <- ggplot( cars, aes(speed, dist)  ) + geom_point() + geom_smooth()
   plotly::ggplotly(f)


Useful shiny functions for Server/UI…

  plotly::renderPlotly()
  plotly::plotlyOutput()

ggiraph

Again, you can build your plots using ggplot and convert. I like this one for linking up plots…below, put your cursor over a dot on the left or a bar on the right. If you know CSS, it can be pretty powerful…

library(ggiraph)
library(tidyverse)
library(patchwork)

mtcars_db <- rownames_to_column(mtcars, var = "carname")

# First plot: Scatter plot
fig_pt <- ggplot(
  data = mtcars_db,
  mapping = aes(
    x = disp, y = qsec,
    tooltip = carname, data_id = carname
  )
) +
  geom_point_interactive(
    size = 3, hover_nearest = TRUE
  ) +
  labs(
    title = "Displacement vs Quarter Mile",
    x = "Displacement", y = "Quarter Mile"
  ) +
  theme_bw()

# Second plot: Bar plot
fig_bar <- ggplot(
  data = mtcars_db,
  mapping = aes(
    x = reorder(carname, mpg), y = mpg,
    tooltip = paste("Car:", carname, "<br>MPG:", mpg),
    data_id = carname
  )
) +
  geom_col_interactive(fill = "skyblue") +
  coord_flip() +
  labs(
    title = "Miles per Gallon by Car",
    x = "Car", y = "Miles per Gallon"
  ) +
  theme_bw()

# Combine the plots using patchwork
 combined_plot <- fig_pt + fig_bar + plot_layout(ncol = 2) 

# Combine the plots using cowplot
# combined_plot <- cowplot::plot_grid(fig_pt, fig_bar, ncol=2) 

# Create a single interactive plot with both subplots
interactive_plot <- girafe(ggobj = combined_plot)

# Set options for the interactive plot
girafe_options(
  interactive_plot,
  opts_hover(css = "fill:cyan;stroke:black;cursor:pointer;"),
  opts_selection(type = "single", css = "fill:red;stroke:black;")
)


Useful shiny functions for Server/UI…

  ggiraph::renderGirafe()
  ggiraph::ggiraphOutput()
  

Let’s get our hands dirty…

Your Task

Build a shiny app to communicate Day 2 analysis results to your client

You can download our version:

Additional resources

For useful resources: https://shiny.posit.co/r/articles/

Good for basic shiny https://mastering-shiny.org/

Engineering Production-Grade Shiny Apps 

https://engineering-shiny.org/index.html 

Beyond showing results

I/O: Uploading files

As your Apps improve in capabilities, you may want to have users upload inputs (e.g. spatial layer, data)

I/O: Saving user inputs

I/O: Downloading

If your app produces output for the user, you may want to have a button so that they can download those results to their computer.

Dashboards

shinydashboards package

This package helps with setting up dashboard layouts (columns, rows, matrix) as well as a suite of html objects (e.g. messages, notifications), aesthetics (“skins”), and other more bespoke tools. Check out https://rstudio.github.io/shinydashboard/ for examples and overview.

flexdashboard package

Flexdashboard can be useful way to quickly create a layout using Rmarkdown-like file. At the simplest, you can create an interactive dashboard with no reactivity. If you want reactivity (e.g. inputs), then you can still use the template and integrate selectInput() functions and renderXXX() functions without explicitly defining ui/server. It is a bit of a hybrid approach between Rmarkdown/Quarto and shiny App. Not sure how much this package is still being developed??

For a range of examples: https://rstudio.github.io/flexdashboard/articles/examples.html

An example as a shiny App run in a Rmarkdown file, see https://jjallaire.shinyapps.io/shiny-ggplot2-diamonds/.

Think about how shiny Apps might be useful

Visualising data for clients

Ibis Tracker - Nev’s masterpiece

https://arisci.shinyapps.io/ibisTracker/

Data sharing - upload

Model simulations